﻿#include "mainwindow.h"
#include <QtSerialPort/QSerialPort>
#include <QDebug>
#include <iostream>




MainWindow::MainWindow(QWidget *parent)
    : QWidget(parent)
{
    this->setFixedSize(800,600);
    setWindowTitle(QString::fromLocal8Bit("串口调试软件"));
    date_init(); //初始化显示的数据
    setUI();   //绘制UI
}

MainWindow::~MainWindow()
{
}

void MainWindow::date_init()
{
    QString baudRatesArray[] = {"1200","2400","4800","9600","14400","19200","38400","56000", "57600", "115200", "128000", "256000","custom"};
    QString serial_check[] = {"None","Odd","Even"};
    QString serial_date[] = {"8","7","6","5"};
    QString serial_stop[] = {"1","1.5","2"};
    int numBaudRates = sizeof(baudRatesArray) / sizeof(*baudRatesArray);
    int i;

    /*获取当前的数据*/
    g_all_ports = get_all_port();
    g_all_bard = new QList<QString> ();
    for ( i = 0; i < numBaudRates; ++i) {
        g_all_bard->append(baudRatesArray[i]);
    }

    numBaudRates = sizeof(serial_check) / sizeof(*serial_check);
    g_all_check = new QList<QString> ();
    for ( i = 0; i < numBaudRates; ++i) {
        g_all_check->append(serial_check[i]);
    }

    numBaudRates = sizeof(serial_date) / sizeof(*serial_date);
    g_all_date = new QList<QString> ();
    for ( i = 0; i < numBaudRates; ++i) {
        g_all_date->append(serial_date[i]);
    }

    g_all_stop = new QList<QString> ();
    numBaudRates = sizeof(serial_stop) / sizeof(*serial_stop);
    for ( i = 0; i < numBaudRates; ++i) {
        g_all_stop->append(serial_stop[i]);
    }

    serial_useing_port = new QSerialPort(this);

    file_max_send_bytes = 256;
    file_send_timer = new QTimer(this);
    connect(file_send_timer, SIGNAL(timeout()), this, SLOT(file_send_by_timer()));
    update_com_port_timer = new QTimer(this);
    connect(update_com_port_timer, SIGNAL(timeout()), this, SLOT(update_com_port_fun()));
    update_com_port_timer->start(8000); //9S刷新一次
    m_file_name_seng_flag =false;

    file_open_last_path = new QSettings("file_save_addr.ini", QSettings::IniFormat);// 文件存储地址
}


void MainWindow::setUI()
{
    label_port = new QLabel(this);
    combo_box_port = new QComboBox(this);
    /*设置串口号属性和位置*/
    QFont label_font;
    label_font.setPointSize(15);
    m_setlabel_style(label_port,0,10,60,20,label_font,"串口号");
    m_setcombox_style(combo_box_port,80,10,160,20,g_all_ports);
    /*设置波特率*/
    label_bard = new QLabel(this);
    combo_box_bard = new QComboBox(this);
    m_setlabel_style(label_bard,0,40,60,20,label_font,"波特率");
    m_setcombox_style(combo_box_bard,80,40,160,20,g_all_bard);
    connect(combo_box_bard, SIGNAL(currentIndexChanged(int)),this, SLOT(select_bard_change(int)));
    /*设置校验位*/
    label_check = new QLabel(this);
    combo_box_check = new QComboBox(this);
    m_setlabel_style(label_check,0,70,60,20,label_font,"检验位");
    m_setcombox_style(combo_box_check,80,70,160,20,g_all_check);
    /*设置数据位*/
    label_date = new QLabel(this);
    combo_box_date = new QComboBox(this);
    m_setlabel_style(label_date,0,100,60,20,label_font,"数据位");
    m_setcombox_style(combo_box_date,80,100,160,20,g_all_date);
    /*设置停止位*/
    label_stop = new QLabel(this);
    combo_box_stop = new QComboBox(this);
    m_setlabel_style(label_stop,0,130,60,20,label_font,"停止位");
    m_setcombox_style(combo_box_stop,80,130,160,20,g_all_stop);
    /*设置打开串口按钮*/
    label_serial_open = new QLabel(this);
    btn_serial_open = new QPushButton(this);
    label_font.setPointSize(16);
    m_setlabel_style(label_serial_open,0,165,90,30,label_font,"串口操作");
    label_font.setPointSize(15);
    btn_serial_open->setGeometry(QRect(90,160,120,30));
    btn_serial_open->setFont(label_font);
    btn_serial_open->setIcon(QPixmap(":/icons/on.jpg"));
    btn_serial_open->setText(QString::fromLocal8Bit("打开串口"));
    connect(btn_serial_open,&QPushButton::clicked,this,&MainWindow::serial_open_btn_fun); //绑定打开串口时的信号和槽函数

    /*串口收发功能定义区*/
    recv_label_asc2 = new QLabel(this);
    m_setlabel_style(recv_label_asc2,0,200,120,30,label_font,"接收区设置");
    gup_btn_check_asc2 = new QButtonGroup(this);
    ra_btn_check_asc2 = new QRadioButton("ASCII",this);
    ra_btn_check_asc2->move(0,230);
    ra_btn_check_asc2->setChecked(true);
    ra_btn_check_not_asc2 = new QRadioButton("HEX",this);
    ra_btn_check_not_asc2->move(60,230);
    gup_btn_check_asc2->addButton(ra_btn_check_asc2);
    gup_btn_check_asc2->addButton(ra_btn_check_not_asc2);
    gup_btn_check_asc2->setExclusive(true);//确保只能有一个被选中
    recv_btn_clean = new QPushButton(QString::fromLocal8Bit("清空接收区"),this);
    recv_btn_clean->move(120,225);
    connect(recv_btn_clean,&QPushButton::clicked,this,&MainWindow::btn_clean_recv_date);

    send_label_asc2 = new QLabel(this);
    m_setlabel_style(send_label_asc2,0,260,120,30,label_font,"发送区设置");
    send_gup_btn_check_asc2 = new QButtonGroup(this);
    send_ra_btn_check_asc2 = new QRadioButton("ASCII",this);
    send_ra_btn_check_asc2->move(0,290);
    send_ra_btn_check_asc2->setChecked(true);
    send_ra_btn_check_not_asc2 = new QRadioButton("HEX",this);
    send_ra_btn_check_not_asc2->move(60,290);
    send_gup_btn_check_asc2->addButton(send_ra_btn_check_asc2);
    send_gup_btn_check_asc2->addButton(send_ra_btn_check_not_asc2);
    send_gup_btn_check_asc2->setExclusive(true);//确保只能有一个被选中
    send_btn_clean = new QPushButton(QString::fromLocal8Bit("清空发送区"),this);
    send_btn_clean->move(120,285);
    connect(send_btn_clean,&QPushButton::clicked,this,&MainWindow::btn_clean_send_date);
    send_btn_date = new QPushButton(QString::fromLocal8Bit("发送数据"),this);
    send_btn_date->move(140,260);
    connect(send_btn_date,&QPushButton::clicked,this,&MainWindow::btn_send_date);

    recv_text_edit = new QPlainTextEdit(this);
    recv_text_edit->setGeometry(QRect(240,0,width() - 240,height() / 2));
    recv_text_edit->setReadOnly(true);

    send_text_edit = new QPlainTextEdit(this);
    send_text_edit->setGeometry(QRect(240,height() / 2,width() - 240,height()));
    connect(serial_useing_port, SIGNAL(readyRead()), this, SLOT(serial_read_fun())); //接收数据的信号和槽函数


    /*文件发送功能定义区*/
    file_send_label = new QLabel(this);
    m_setlabel_style(file_send_label,0,320,120,30,label_font,"文件发送区");
    file_send_pross_bar = new QProgressBar(this);
    file_send_pross_bar->setGeometry(QRect(30,350,200,20));
    file_name_label = new QLabel(this);
    label_font.setPointSize(12);
    m_setlabel_style(file_name_label,0,390,40,30,label_font,"文件:");
    file_line_edit = new QLineEdit(this);
    file_line_edit->setGeometry(QRect(40,380,180,30));
    file_line_edit->setReadOnly(true);

    file_select_btn = new QPushButton(this);
    file_select_btn->setText(QString::fromLocal8Bit("选择文件"));
    file_select_btn->move(0,420);
    connect(file_select_btn,&QPushButton::clicked,this,&MainWindow::file_select_btn_fun);



    file_send_btn = new QPushButton(this);
    file_send_btn->setText(QString::fromLocal8Bit("发送文件"));
    file_send_btn->move(120,420);
    connect(file_send_btn,&QPushButton::clicked,this,&MainWindow::file_send_btn_fun);
}

void MainWindow::select_bard_change(int index)
{
    if (12 == index) {
        QLineEdit *lineEdit = new QLineEdit(this);
        combo_box_bard->setLineEdit(lineEdit);
        lineEdit->clear();
    } else {
        combo_box_bard->setEditable(false);
    }
}

QList<QString>* MainWindow::get_all_port()
{
    QList<QString> *ports = new QList<QString>();
    const auto infos = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo &info : infos) {
        ports->append(info.portName() + ":" + info.description());
    }
    return ports;
}

void MainWindow::m_setlabel_style(QLabel *setLable,int x,int y,int w, int h,QFont labelFont,const char* text) {
    setLable->setGeometry(QRect(x,y,w,h));
    setLable->setFont(labelFont);
    setLable->setText(QString::fromLocal8Bit(text));
    setLable->setAlignment(Qt::AlignLeft);    //设置文字居中显示Qt::AlignCenter
}

void MainWindow::m_setcombox_style(QComboBox *box,int x,int y,int w, int h,QList<QString> *items)
{
    box->setGeometry(QRect(x,y,w,h));
    for (const QString &str : *items) {
        box->addItem(str);
    }
}

void MainWindow::serial_open_btn_fun()
{
    if (btn_serial_open->text().toLocal8Bit() == "打开串口") { //未被打开，执行打开操作
        if (!serial_open()) {
            btn_serial_open->setIcon(QPixmap(":/icons/close.jpg"));
            btn_serial_open->setText(QString::fromLocal8Bit("关闭串口"));
            //停止刷新串口
            update_com_port_timer->stop();
        }
    } else { //表示当前串口被打开了，执行关闭操作
        btn_serial_open->setIcon(QPixmap(":/icons/on.jpg"));
        btn_serial_open->setText(QString::fromLocal8Bit("打开串口"));
        if(serial_useing_port->isOpen())serial_useing_port->close();
        update_com_port_timer->start(8000);
    }
}


int MainWindow::serial_open()
{
    int ret = 0;
    //获取选择的串口

    serial_useing_port->setPortName(combo_box_port->currentText().section(":",0,0));

    serial_useing_port->setBaudRate(combo_box_bard->currentText().toInt());

    if (combo_box_check->currentText() == "None") {
        serial_useing_port->setParity(QSerialPort::NoParity);
    } else if (combo_box_check->currentText() == "Odd") {
        serial_useing_port->setParity(QSerialPort::OddParity);
    } else if (combo_box_check->currentText() == "Even") {
        serial_useing_port->setParity(QSerialPort::EvenParity);
    }

    switch (combo_box_date->currentText().toInt()) {
        case 8:serial_useing_port->setDataBits(QSerialPort::Data8);break;
        case 7:serial_useing_port->setDataBits(QSerialPort::Data7);break;
        case 6:serial_useing_port->setDataBits(QSerialPort::Data6);break;
        case 5:serial_useing_port->setDataBits(QSerialPort::Data5);break;
    }

    if (combo_box_stop->currentText() == "1") {
        serial_useing_port->setStopBits(QSerialPort::OneStop);
    } else if (combo_box_stop->currentText() == "1.5") {
        serial_useing_port->setStopBits(QSerialPort::OneAndHalfStop);
    } else if (combo_box_stop->currentText() == "2") {
        serial_useing_port->setStopBits(QSerialPort::TwoStop);
    }

    serial_useing_port->setFlowControl(QSerialPort::NoFlowControl);

    if (serial_useing_port->open(QIODevice::ReadWrite) == false) { //打开失败
        QMessageBox::critical(this, "Warning", QString::fromLocal8Bit("串口不存在或被其他程序占用"));
        ret = -1;
    }
    return ret;
}

void MainWindow::serial_read_fun()
{
    QByteArray buf = serial_useing_port->readAll(); //从串口读取信息
    if (ra_btn_check_asc2->isChecked()) { //选中了ASCII选项
        recv_text_edit->insertPlainText(QString::fromLocal8Bit(buf));
    } else if (ra_btn_check_not_asc2->isChecked()){ //HEX选项
        QDataStream out(&buf, QIODevice::ReadWrite); //读取数据
        while(!out.atEnd()) //读取是否完成
        {
            qint8 outChar = 0;
            out >> outChar;
            QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')); //转换16进制
            recv_text_edit->insertPlainText(str+" "); //每显示一次后面加一个空格
        }
    }
}
void MainWindow::btn_send_date()
{
    QString sendstr=send_text_edit->toPlainText();  //获取将要发送数据
    if(ra_btn_check_asc2->isChecked())  // 如果发送ASCII模式
    {
        QByteArray SendTextByte = sendstr.toLocal8Bit(); //将发送的数据转换格式
        serial_useing_port->write(SendTextByte); // 通过串口将数据发送出去
    } else if(ra_btn_check_not_asc2->isChecked())  // 如果发送HEX
    {
        QByteArray SendTextByte = QByteArray::fromHex(sendstr.toLatin1());  // 转换数据格式
        serial_useing_port->write(SendTextByte); // 通过串口将数据发送出去
    }
}

void MainWindow::btn_clean_recv_date()
{
    recv_text_edit->clear();
}

void MainWindow::btn_clean_send_date()
{
    send_text_edit->clear();
}

void MainWindow::file_select_btn_fun()
{
    QString FilePath = file_open_last_path->value("/file/addr").toString();
    file_useing_name = QFileDialog::getOpenFileName(this, "Open a file", QString(FilePath), "files (*)");
    file_line_edit->setText(file_useing_name);
    if (!file_useing_name.isEmpty())file_open_last_path->setValue("/file/addr", file_useing_name);// 保存到配置文件中去
}

void MainWindow::file_send_btn_fun()
{
    if (file_send_btn->text().toLocal8Bit() == "发送文件") {
        //重置进度条
        if(!serial_useing_port->isOpen()) {
            QMessageBox::critical(this, "Warning", QString::fromLocal8Bit("请先打开串口再发送文件"));
            return;
        }
        if(file_useing_name.isEmpty()) {
            QMessageBox::critical(this, "Warning", QString::fromLocal8Bit("未选择文件或文件不存在"));
            return;
        }
        file_sending = new QFile(file_useing_name);
        if (!file_sending->open(QIODevice::ReadOnly)) {
            QMessageBox::critical(this, "Warning", QString::fromLocal8Bit("文件打开失败"));
            return;
        }

        file_total_bytes = file_sending->size();

        /*开启定时器*/
        file_send_timer->start(100); //100ms判断一次数据的收发
        file_send_pross_bar->setValue(0);
        file_send_pross_bar->setMaximum(file_total_bytes);
        file_send_over = false;
        m_file_name_seng_flag =false;
        file_have_send_bytes = 0;
        file_send_btn->setText(QString::fromLocal8Bit("停止发送"));
    } else {
        file_send_timer->stop();
        file_send_btn->setText(QString::fromLocal8Bit("发送文件"));

    }

}
QByteArray Hexstring2QByteArray(QString str)
{
    QByteArray Hexstr;
    bool ok;
    for (int i = 0; i < str.size(); i += 2)
    {
        Hexstr.append((char)str.mid(i, 2).toInt(&ok, 16));
     }
     return Hexstr;
}

void MainWindow::file_send_by_timer()
{
    qint64 m_file_once_send_bytes = 0;
    qint64 m_file_residue_send_bytes = 0; //剩余待发送的字节数
    QFileInfo *m_send_file = new QFileInfo(file_useing_name);
    quint64 sum_crc=0;

    //AB 85 B2 07 73 65 74 2E 62 69 6E 4C

    QByteArray file_send_head;
    file_send_head.append(static_cast<char>(0xAB));
    file_send_head.append(static_cast<char>(0x85));

    if (file_have_send_bytes == 0 && !m_file_name_seng_flag) {//起始帧,需要发送保存在下位机的文件路径和文件名，eg: /lgvl/icon/test.bin
        file_send_head.append(static_cast<char>(0xB0)); //拼接CMD
        QString m_file_all_name = "/lvgl/icon/" + m_send_file->fileName(); //拼接名字
        file_send_head.append(m_file_all_name.length());
        //将文件名字符串变成16进制数据
        file_send_head.append(m_file_all_name.toLocal8Bit());
        QByteArray m_file_Byte= Hexstring2QByteArray(m_file_all_name.toLocal8Bit().toHex());
        for (const auto &byte : m_file_Byte) {
            int test = static_cast<int>(byte);
            sum_crc += test;
        }
        sum_crc = ~sum_crc;
        file_send_head.append(static_cast<char>(sum_crc)); //拼接校验值
        m_file_name_seng_flag =true;
    } else { //其他帧
        m_file_residue_send_bytes = file_total_bytes - file_have_send_bytes;
        if (m_file_residue_send_bytes <= file_max_send_bytes) {       //最后一帧数据
            file_send_head.append(static_cast<char>(0xB2)); //拼接CMD，最后一帧的数据
            file_send_over = true;
        } else {
            file_send_head.append(static_cast<char>(0xB1)); //拼接CMD,正常发送的数据
        }
        m_file_once_send_bytes = qMin(file_total_bytes - file_have_send_bytes, (qint64)255); //每次最多发送255个字节
        file_send_head.append(static_cast<char>(m_file_once_send_bytes & 0xFF));
        QByteArray fileData = file_sending->read(m_file_once_send_bytes);
        for (const auto &byte : fileData) {
            int test = static_cast<int>(byte);
            sum_crc += test;
        }
        file_send_head += fileData;
        sum_crc = ~sum_crc;
        file_send_head.append(static_cast<char>(sum_crc)); //拼接校验值
        file_have_send_bytes +=m_file_once_send_bytes;
    }

    serial_useing_port->write(file_send_head);
    file_send_pross_bar->setValue(file_have_send_bytes); //更新进度显示

    if (file_send_over) { //表示发送完成
        file_send_timer->stop();
        file_send_btn->setText(QString::fromLocal8Bit("发送文件"));
    }
}

void MainWindow::update_com_port_fun()
{
    g_all_ports->clear();
    combo_box_port->clear();
    g_all_ports = get_all_port();
    m_setcombox_style(combo_box_port,80,10,160,20,g_all_ports);
}
